package org.mimizet.codegen;

import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.body.Parameter;
import org.simpleframework.xml.*;
import org.simpleframework.xml.convert.AnnotationStrategy;
import org.simpleframework.xml.core.Commit;
import org.simpleframework.xml.core.Persister;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * User: Admin
 * Date: 29.08.11
 * Time: 20:42
 */


@Root
public class Mimizet {
    public static final Logger logger = LoggerFactory.getLogger(Mimizet.class);

    @ElementList(inline = true)
    public ArrayList<Class> classes = new ArrayList<Class>();

    public static class Class {
        @Attribute
        public String name;

        @ElementList(inline = true)
        public ArrayList<Method> methods = new ArrayList<Method>();
    }

    private static boolean equalParameters(List<Parameter> parameters, Method method) {

        Iterator<Argument> argumentIterator = method.arguments.iterator();
        Iterator<Parameter> parameterIterator = parameters.iterator();

        boolean equal = true;

        while (parameterIterator.hasNext() && argumentIterator.hasNext()) {
            Parameter nextParameter = parameterIterator.next();
            Argument nextArgument = argumentIterator.next();

            if (!nextArgument.type.equals(nextParameter.getType().toString())) {
                equal = false;
                break;
            }
        }

        if (equal && !parameterIterator.hasNext() && !argumentIterator.hasNext()) {
            return true;
        }
        return false;
    }


    public static final Pattern ARGUMENTS_PATTERN = Pattern.compile("^(.*)\\(([^)]+)\\)$");

    public static class Method {
        @Attribute
        public String name;

        @Attribute(required = false)
        public String converter;

        @Transient
        ArrayList<Argument> arguments = new ArrayList<Argument>(4);

        @Transient
        String returns;

        @Commit
        public void init() {
            Matcher matcher = ARGUMENTS_PATTERN.matcher(name);

            if (matcher.find()) {
                name = matcher.group(1);
                String argumentsS = matcher.group(2);
                String[] argumentTypes = argumentsS.split(",\\s*");
                for (int i = 0; i < argumentTypes.length; i++) {
                    arguments.add(new Argument(argumentTypes[i]));
                }
            }
        }

        public boolean findMethod(ArrayList<MethodDeclaration> methodDeclarations) {
            boolean found = false;
            for (MethodDeclaration methodDeclaration : methodDeclarations) {
                if (name.equals(methodDeclaration.getName())) {
                    logger.debug("found name match for method: {}", name);

                    if (equalParameters(methodDeclaration.getParameters(), this)) {
                        returns = methodDeclaration.getType().toString();
                        found = true;
                        break;
                    }
                }
            }

            return found;
        }
    }

    public static class Argument {
        public Argument(String type) {
            this.type = type;
        }

        String type;

        @Override
        public String toString() {
            return type;
        }
    }

    public String toXml() {
        StringWriter out = new StringWriter();
        try {
            new Persister(new AnnotationStrategy()).write(this, out);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return out.toString();
    }

    public static Mimizet parseXml(File file) throws Exception {
        Serializer serializer = new Persister();

        return serializer.read(Mimizet.class, file);
    }
}